package cn.kginglam.dynamic_excel.strategy.impl.importExcel2Database;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.kginglam.dynamic_excel.common.constant.KeyConstant;
import cn.kginglam.dynamic_excel.common.utils.*;
import cn.kginglam.dynamic_excel.model.entity.StudentInfo;
import cn.kginglam.dynamic_excel.model.excel.ImportModule;
import cn.kginglam.dynamic_excel.model.excel.StudentInfoImportModule;
import cn.kginglam.dynamic_excel.service.StudentInfoService;
import cn.kginglam.dynamic_excel.strategy.ImportExcel2DatabaseStrategy;
import cn.kginglam.dynamic_excel.strategy.param.ImportExcel2DatabaseParam;
import com.google.common.collect.Lists;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author: Lam
 * @date: 2021-09-29 17:03
 * @description:
 */
public class StudentInfoImportExcel2DatabaseStrategy implements ImportExcel2DatabaseStrategy {


    private RedisUtils redisUtils;
    private RedisBloomFilterUtils redisBloomFilterUtils;
    private StudentInfoService studentInfoService;
    private BloomFilterHelper bloomFilterHelper;

    @Override
    public void setParam(ImportExcel2DatabaseParam param) {
        this.redisUtils = param.getRedisUtils();
        this.redisBloomFilterUtils = param.getRedisBloomFilterUtils();
        this.studentInfoService = param.getStudentInfoService();
        this.bloomFilterHelper = param.getBloomFilterHelper();
    }

    @Override
    public void importExcel2Database(List importModule) {


        if (CollectionUtil.isEmpty(importModule)) {
            return;
        }


        // 1.数据拆分
        List<List<StudentInfoImportModule>> partitionList = Lists.partition(importModule, importModule.size() / 10);


        // 3.埋点标记，做为导入完成的标记
        String importFinishFlagKey = KeyConstant.substitute(KeyConstant.STUDENT_EXCEL_IMPORT_FINISH_FLAG, "1");
        redisUtils.set(importFinishFlagKey, partitionList.size());


        // 3.多线程数据校验
        partitionList.forEach(dataList -> ThreadPoolUtils.THREAD_POOL_EXECUTOR.execute(() -> {


            // TODO CompletableFuture单独使用其他线程池（不允许用main或ThreadPoolUtils.THREAD_POOL_EXECUTOR线程池）
            // TODO 因为join的时候可能导致其他线程阻塞
            // 当前批次的手机号码（数据库中的手机号码）
            CompletableFuture<String> existPhoneKeyFuture = CompletableFuture.supplyAsync(() ->
                    this.existPhoneKey(dataList));

            // 当前批次的证件号码（数据库中的证件号码）
            CompletableFuture<String> existCardNoFuture = CompletableFuture.supplyAsync(() ->
                    this.existCardNoKey(dataList));


            // TODO 真正的检验不止手机号码和证件，所以当前先用join，后续改用线程编排框架
            String existPhoneKey = existPhoneKeyFuture.join();
            String existCardNoKey = existCardNoFuture.join();


            // 成功的数据
            List<StudentInfoImportModule> successDataList = new ArrayList<>();
            // TODO 这里可以用线程编排框架做异步化
            dataList.forEach(data -> {

                // 2.1.当前手机号码数据库是否已经存在（并行校验）
                verifyPhoneExist(data, existPhoneKey);

                // 2.2.当前证件号码数据库是否已经存在（并行校验）
                verifyCardNoExist(data, existCardNoKey);


                // 3.1 收集校验成功的数据
                if (!NumberUtil.equals(0, data.getExceptionCode())) {
                    successDataList.add(data);
                }
                // 3.2 收集校验失败的数据
                else {
                    // 失败的数据放进 Redis -zSet里面排序
                    String errorDataKey = KeyConstant.substitute(KeyConstant.STUDENT_EXCEL_ERROR_DATA, "1");
                    redisUtils.zAdd(errorDataKey, data, data.getCurrentRowNum());
                }
            });

            // 成功的数据入库
            studentInfoService.batchSave(StudentInfo.batchTransition(dataList));

            // 完成标记
            Integer finish = (Integer) redisUtils.get(importFinishFlagKey);
            redisUtils.set(importFinishFlagKey, finish - 1);
        }));

    }


    /**
     * 将存在的手机号码存放在Bloom过滤器里面
     *
     * @param studentInfoImportModuleList excel 里面的数据
     * @return bloom-key,当返回""时证明 studentInfoImportModuleList 没有重复数据
     */
    private String existPhoneKey(List<StudentInfoImportModule> studentInfoImportModuleList) {
        List<String> phoneList = studentInfoImportModuleList.stream().map(StudentInfoImportModule::getPhone).collect(Collectors.toList());
        List<String> existPhoneList = studentInfoService.listPhoneByPhoneList(phoneList);
        if (CollectionUtil.isNotEmpty(existPhoneList)) {
            String existPhoneKey = RandomUtil.randomString(10);
            existPhoneList.forEach(existPhone -> redisBloomFilterUtils.addByBloomFilter(bloomFilterHelper, existPhoneKey, existPhone));
            return existPhoneKey;
        }
        return "";
    }


    /**
     * 将存在的证件号码存放在Bloom过滤器里面
     *
     * @param studentInfoImportModuleList excel 里面的数据
     * @return bloom-key,当返回""时证明 studentInfoImportModuleList 没有重复数据
     */
    private String existCardNoKey(List<StudentInfoImportModule> studentInfoImportModuleList) {
        List<String> cardNoList = studentInfoImportModuleList.stream().map(StudentInfoImportModule::getCardNo).collect(Collectors.toList());
        List<String> existCardNoList = studentInfoService.listCardNoByCardNoList(cardNoList);
        if (CollectionUtil.isNotEmpty(existCardNoList)) {
            String existCardNoKey = RandomUtil.randomString(10);
            existCardNoList.forEach(existCardNo -> redisBloomFilterUtils.addByBloomFilter(bloomFilterHelper, existCardNoKey, existCardNo));
            return existCardNoKey;
        }

        return "";
    }


    /**
     * 校验手机号码是否存在
     *
     * @param studentInfoImportModule
     * @param bloomKey
     */
    private synchronized void verifyPhoneExist(StudentInfoImportModule studentInfoImportModule, String bloomKey) {
        boolean exist = redisBloomFilterUtils.includeByBloomFilter(bloomFilterHelper, bloomKey, studentInfoImportModule.getPhone());
        if (exist) {
            // 添加错误码和错误信息
            studentInfoImportModule.addExceptionCode(BitStatesUtils.BitStates.STUDENT_PHONE_REPETITION);
            String errorInfo = studentInfoImportModule.getExceptionInfo();
            studentInfoImportModule.setExceptionInfo(StrUtil.isBlank(errorInfo) ? "手机号码重复" : errorInfo + "," + "手机号码重复");
        }
    }


    /**
     * 校验证件号码是否存在
     *
     * @param studentInfoImportModule
     * @param bloomKey
     */
    private synchronized void verifyCardNoExist(StudentInfoImportModule studentInfoImportModule, String bloomKey) {
        boolean exist = redisBloomFilterUtils.includeByBloomFilter(bloomFilterHelper, bloomKey, studentInfoImportModule.getCardNo());
        if (exist) {
            // 添加错误码
            studentInfoImportModule.addExceptionCode(BitStatesUtils.BitStates.STUDENT_CARD_NO_REPETITION);
            String errorInfo = studentInfoImportModule.getExceptionInfo();
            studentInfoImportModule.setExceptionInfo(StrUtil.isBlank(errorInfo) ? "证件号码重复" : errorInfo + "," + "证件号码重复");
        }
    }
}
